package com.gotokeep.gyromousedemo

import android.util.Log
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import java.net.DatagramPacket
import java.net.DatagramSocket
import java.net.InetAddress
import java.nio.charset.Charset
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.math.abs

object DataReceiver {

    private const val BUFFER_SIZE = 1024
    private const val TAG = "DataReceiver"

    var dataCallback: DataCallback? = null

    private val localCoroutineScope = CoroutineScope(EmptyCoroutineContext)

    init {
        val buffer = ByteArray(BUFFER_SIZE)
        val packet = DatagramPacket(buffer, buffer.size)
        val socket = DatagramSocket(AppContext.PORT)

        localCoroutineScope.launch {
            while (isActive) {
                kotlin.runCatching {
                    socket.receive(packet)
                    val receivedData = ReceivedData(
                        packet.data.copyOfRange(0, packet.length),
                        packet.address,
                        packet.port
                    )
                    Log.e(TAG, "receivedData->$receivedData")
                    dataCallback?.invoke(receivedData)
                }
            }
        }
    }

    fun release() {
        localCoroutineScope.cancel()
    }
}

typealias DataCallback = (ReceivedData) -> Unit

data class ReceivedData(val data: ByteArray, val address: InetAddress, val port: Int) {

    val positions: FloatArray
        get() {
            if (positionsInner == null) {
                handleData()
            }
            return positionsInner!!
        }

    val eventType: Int
        get() {
            if (eventTypeInner == null) {
                handleData()
            }
            return eventTypeInner!!
        }

    private var positionsInner: FloatArray? = null
    private var eventTypeInner: Int? = null

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is ReceivedData) return false

        if (!data.contentEquals(other.data)) return false
        if (address != other.address) return false
        if (port != other.port) return false

        return true
    }

    override fun hashCode(): Int {
        var result = data.contentHashCode()
        result = 31 * result + address.hashCode()
        result = 31 * result + port
        return result
    }

    override fun toString(): String {
        return "ReceivedData(data=${data.toString(Charset.forName("utf-8"))}, address=$address, port=$port)"
    }

    /**
     * 判断 other 表示的坐标与当前对象的坐标对比后，是否是有效的鼠标移动，true 表示 是 有效移动，false 表示不是
     */
    fun validChange(other: FloatArray): Boolean {
        for (i in positions.indices) {
            if (abs(positions[i] - other[i]) > MIN) {
                return true
            }
        }
        return false
    }

    private fun handleData() {
        val receivedString = data.toString(Charset.forName("utf-8"))
        val resultArray = receivedString
            .split(AppContext.DIVIDER)
        eventTypeInner = resultArray[0].toInt()
        positionsInner =
            resultArray.subList(1, resultArray.size).map { it.toFloat() }.toFloatArray()
    }

    companion object {

        private const val MIN = 0.001f
    }
}
